package com.common.log;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import com.common.constant.CommonConstant;
import com.common.constant.LoggerConstant;
import com.common.util.IPUtil;
import com.common.util.MyDateUtil;
import com.common.vo.LoginUser;
import com.common.vo.Result;
import com.modules.entity.sys.LogEntity;
import com.modules.service.sys.LogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//import com.fasterxml.jackson.databind.ObjectMapper;

/**
 * AOP切面日志持久化处理
 * 
 * @author YYQ
 *
 */
@Component // 对象由spring管理
@Aspect // 切面注解
public class LogAspect {

	@Autowired
	private LogService logService;

	private final Logger logger = LoggerFactory.getLogger(LogAspect.class);
	// 队列
	private static BlockingQueue<LogEntity> queue = new LinkedBlockingQueue<LogEntity>();
	// 缓存线程池 （大小根据需求调整）
	private static ExecutorService threadPool = Executors.newFixedThreadPool(2);
	// 任务数（大小根据需求调整）
	private static int taskSize = 3;
	// 正常-线程是否已启动
	boolean isStartThread = false;
	// 异常-线程是否已启动
	boolean exStartThread = false;
	// 用来启动或停止线程
	static boolean run = true;

	public static BlockingQueue<LogEntity> getQueue() {
		return queue;
	}

	public static void setQueue(BlockingQueue<LogEntity> queue) {
		LogAspect.queue = queue;
	}

	public static boolean isRun() {
		return run;
	}

	public static void setRun(boolean run) {
		LogAspect.run = run;
	}

	/**
	 * 定义切入点，只要方法添加了LogData的都会进入日志处理
	 */
	@Pointcut("@annotation(com.common.log.LogData)") // LogData是切入指定包路径的注解
	public void pointcut() {
		System.out.println("切入点...");
	}

	/**
	 * 正常数据时，走这个方法
	 * 
	 * @param joinPoint
	 */
	@AfterReturning(value = "pointcut()", returning = "res")
	public void afterReturning(JoinPoint joinPoint, Object res) {
		try {
			if(res instanceof Result) {
				Result<?> r = (Result<?>) res;
				if(!r.getSuccess() || r.getCode().equals(Result.FAIL_STATE) || r.getCode().equals(Result.FAIL_CODE)) {
					return;
				}
			}
			// 获取到方法签名
			MethodSignature signature = (MethodSignature) joinPoint.getSignature();
			// 请求的controller层路径
			String targetName = joinPoint.getTarget().getClass().getName();
			// 获取到连接点方法对象
			Method method = signature.getMethod();
			// 获取方法上面特定的注解
			LogData annotation = method.getAnnotation(LogData.class);
			//logger.info("-------------获取到注解内容：logType=" + annotation.remarks() + ",description:" + annotation.module());
			// aop中获取request
			ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
					.getRequestAttributes();
			HttpServletRequest request = requestAttributes.getRequest();
			HttpSession session = request.getSession();
			// 获取操作人
			LoginUser user = (LoginUser) session.getAttribute(CommonConstant.LOGIN_USER);
			// 获取请求数据 TODO（已使用线程内解析参数成json格式存储）
			// Map<String, String[]> parameterMap = request.getParameterMap();
			// 将对象转换成json字符串==>存储到请求数据字段中
			// jackSon json字符串操作
			// ObjectMapper objectMapper = new ObjectMapper();
			// String s = objectMapper.writeValueAsString(parameterMap);
			// logger.info("请求数据："+s);
			if (user == null){
				return;
			}
			LogEntity log = new LogEntity();
			log.setIp(IPUtil.getIpAddr(request));
			log.setAddr(IPUtil.getCityInfo(log.getIp()));
			log.setOid(user.getLastOid());
			log.setUserId(user.getId());
			log.setCreateId(user.getId());
			log.setUpdateId(user.getId());
			log.setUserName(user.getName());
			log.setClassUrl(targetName);
			log.setMethod(method.getName());
			log.setMethodType(request.getMethod());
			log.setModuleName(annotation.module());
			log.setModuleOperation(annotation.remarks());
			// log.setParam(s); // TODO（已使用线程内解析参数成json格式存储）
			log.setType(CommonConstant.ONE);
			String date = MyDateUtil.getStrCurrentDate(MyDateUtil.sfh_ymdhms);
			log.setCreateDate(date);
			log.setUpdateDate(date);
			// logService.save(log);
			// 放入队列
			// queue.put(log);
			new analysisThread(request, log).start();
			if (!isStartThread) {
				for (int i = 0; i < taskSize; i++) {// 初始化任务线程总数
					threadPool.execute(new saveLogThread());
				}
				isStartThread = true;
			}
		} catch (Exception ex) {
			logger.error(LoggerConstant.ERROR_MSG, ex);
		}
	}

	/**
	 * 发生异常时，走这个方法
	 * 
	 * @param joinPoint
	 * @param e
	 */
	@AfterThrowing(pointcut = "pointcut()", throwing = "e")
	public void afterThrowing(JoinPoint joinPoint, Throwable e) {
		try {
			String exMsg = StrUtil.isNotEmpty(e.toString())?e.toString():(e.getCause() != null?e.getCause().toString():e.getMessage());
			if (exMsg != null) {
				// 获取到方法签名
				MethodSignature signature = (MethodSignature) joinPoint.getSignature();
				// 请求的controller层路径
				String targetName = joinPoint.getTarget().getClass().getName();
				// 获取到连接点方法对象
				Method method = signature.getMethod();
				// 获取方法上面特定的注解
				LogData annotation = method.getAnnotation(LogData.class);
				// logger.info("获取到注解内容：logType=" + annotation.remarks() + ",description:" +
				// annotation.module());
				// aop中获取request
				ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder
						.getRequestAttributes();
				HttpServletRequest request = requestAttributes.getRequest();
				HttpSession session = request.getSession();
				// 获取操作人
				LoginUser user = (LoginUser) session.getAttribute(CommonConstant.LOGIN_USER);
				// logger.info("请求数据："+s);
				LogEntity log = new LogEntity();
				log.setIp(IPUtil.getIpAddr(request));
				log.setAddr(IPUtil.getCityInfo(log.getIp()));
				log.setOid(user.getLastOid());
				log.setUserId(user.getId());
				log.setCreateId(user.getId());
				log.setUpdateId(user.getId());
				log.setUserName(user.getName());
				log.setClassUrl(targetName);
				log.setMethod(method.getName());
				log.setMethodType(request.getMethod());
				log.setModuleName(annotation.module());
				log.setModuleOperation(annotation.remarks());
				log.setType(CommonConstant.ONE);
				log.setRemarks(exMsg);
				// logService.save(log);
				// 放入队列
				// queue.put(log);
				new analysisThread(request, log).start();
				if (!exStartThread) {
					new Thread(new saveLogThread()).start();// 异常数据不放入线程池 exStartThread = true;
				}
			}
		} catch (Exception ex) {
			logger.error(LoggerConstant.ERROR_MSG, ex);
		}
	}

	/**
	 * 解析参数-线程
	 *
	 */
	class analysisThread extends Thread {

		private HttpServletRequest request;
		private LogEntity log;

		public analysisThread(HttpServletRequest request, LogEntity log) {
			this.request = request;
			this.log = log;
		}

		@Override
		public void run() {
			try {
				log.setParam(getParam());
				queue.put(log);
			} catch (Exception e) {
				logger.error(LoggerConstant.ERROR_MSG, e);
			}
		}

		/**
		 * 返回json参数
		 * 
		 * @return
		 */
		@SuppressWarnings("rawtypes")
		public String getParam() {
			JSONObject paramJson = new JSONObject();
			Map<String, String[]> map = request.getParameterMap();
			Set<Entry<String, String[]>> keSet = map.entrySet();
			for (Iterator<Entry<String, String[]>> itr = keSet.iterator(); itr.hasNext();) {
				Entry me = (Entry) itr.next();
				Object ok = me.getKey();
				Object ov = me.getValue();
				String[] value = new String[1];
				if (ov instanceof String[]) {
					value = (String[]) ov;
					paramJson.put(ok + "", value);
					for (int k = 0; k < value.length; k++) {
						paramJson.put(ok + "", value[k]);
					}
				} else {
					value[0] = ov.toString();
					paramJson.put(ok + "", ov.toString());
				}
			}
			// System.out.println(paramJson.toJSONString());
			return paramJson.toJSONString();
		}
	}

	/**
	 * 
	 * 异步保存日志-线程
	 */
	class saveLogThread implements Runnable {
		Lock lock = new ReentrantLock();

		@Override
		public void run() {
			try {
				while (run) {// 使用死循环休眠3秒的方式重复利用线程
					while (queue.size() != 0) {
						// 如果对插入顺序无要求，此处不需要同步可提升效率
						lock.lock();
						LogEntity log = queue.take(); // 相关queue
						// 参考网址：https://blog.csdn.net/tonywu1992/article/details/83419448
						logService.save(log);
						lock.unlock();
					}
					Thread.sleep(3000);
				}
			} catch (InterruptedException e) {
				logger.error(LoggerConstant.ERROR_MSG + "saveLogThread被唤醒：", e);
			} catch (Exception e) {
				logger.error(LoggerConstant.ERROR_MSG + "saveLogThread异常：", e);
			}
		}
	}
	
	
	
	
	
	

}